﻿#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>

int Problem1_Proc1(unsigned int num)
{
	int count = 0;
	/*有符号强转成无符号，解决负数的问题*/
	while (num)
	{
		if (num % 2 == 1) count++;
		num /= 2;
	}
	return count;
}
int Problem1_Proc2(int num)
{
	int count = 0;
	for (int i = 0; i < 32; i++)
	{
		if ((num >> i) & 1 == 1) count++;
	}
	return count;
}
int Problem1_Proc3(int num)
{
	/*
		Brian Kernighan 算法:n=n&(n-1); 把n的二进制最右边的1去掉
	*/
	int count = 0;
	while (num)
	{
		num &= (num - 1);
		count++;
	}
	return count;
	//if (num &(num - 1) == 0) printf("num是2的次方数\n");
}
void Problem1_Proc(void)
{
	int a = 0;
	int b = 0;
	int i = 0;
	a = a | (1 << i);	//置1
	b = b & ~(1 << i);	//清0
}
/*
	0开头的数字，会被当做8进制。
	对于整形来说：数据存放内存中其实存放的是补码。
	正整数的原、反、补码都相同。
	负整数的三种表⽰⽅法各不相同。
	原码：直接将数值按照正负数的形式翻译成⼆进制得到的就是原码。符号位都是⽤0表⽰“正”，⽤1表⽰“负”。
	反码：将原码的符号位不变，其他位依次按位取反就可以得到反码。
	补码：反码+1就得到补码。
	补码得到原码也是可以使⽤：取反，+1的操作;原路返回；（两种方式符号位不变）
	减一载取反等效于取反加一，原码到补码和补码到原码都是符号位不变，取反+1

	移位操作符的操作数只能是整数，并且不会改变本身，负数移动补码，但是打印的是原码
	左移一位有*2的效果，对正负数均成立,但是不能移动负数位
	下面采取那种方式取决于编译器
	1. 逻辑右移：左边⽤0填充，右边丢弃
	2. 算术右移：左边⽤原该值的符号位填充，右边丢弃（通常）
	用整数补码进行位操作，用整数的原码来显示；
*/
int main(void)
{
	{
		int a = -2;
		printf("%d\n", a >> 1);// 算术右移
		int b = 0;
		printf("%d\n", ~b);
		printf("%d\n", ~b);
		/*
			^的妙用
			不能创建临时变量（第三个变量），实现两个整数的交换。
		*/
		printf("交换前a=%d,b=%d\n", a, b);
		/*有可能会超出数据类型MAX*/
		a = a + b;
		b = a - b;
		a = a - b;
		printf("交换后a=%d,b=%d\n", a, b);
		printf("交换前a=%d,b=%d\n", a, b);
		/*
			0^a=a;
			a^a=0;
			异或是支持交换律的，不存在进位，不会溢出
		*/
		a = a ^ b; //执行效率不高
		b = a ^ b; //a^b^b=a^0=a
		a = a ^ b; //a^b^a=b^0=b
		printf("交换后a=%d,b=%d\n", a, b);
	}
	/*
		求⼀个整数存储在内存中的⼆进制中1的个数(看补码)
	*/
	{
		int num = 0;
		int out = 0;
		int scanf_cnt = 0;
		scanf_cnt=scanf("%d", &num);
		//out=Problem1_Proc1(num);
		//out = Problem1_Proc2(num);
		out = Problem1_Proc3(num);
		printf("%d\n", out);
	}
	
	/*
		在单目操作符号中：
		&：取地址操作符
		*：解引用操作符 
	*/
	/*
		逗号表达式，从左向右依次执⾏。整个表达式的结果是最后⼀个表达式的结果。
		一定要从左向右计算，因为前面表达式的结果可能会影响到后面的结果
	*/
	{
		int a = 1;
		int b = 2;
		int c = (a > b, a = b + 10, a, b = a + 1); 
		int d = 0;
		printf("%d\n", c);
		if (a = b + 1, c = a / 2, d > 0);
		/*
			{	
				a = get_val();
				count_val(a);
				while (a > 0)
				{
					//业务处理
					//...
					a = get_val();
					count_val(a);
				}	
			}
			{
				while (a = get_val(), count_val(a), a>0)
				{
					//业务处理
				}
			}
		*/
	}
	/*
		 [ ] 下标引⽤操作符:
			操作数：⼀个数组名 + ⼀个索引值(下标)
		 (  )函数调⽤操作符:至少有一个操作符就是函数名
			接受⼀个或者多个操作数：第⼀个操作数是函数名，剩余的操作数就是传递给函数的参数
	*/
	struct student {
		char name[20];
		int age;
		float score;
	}s1, s2, s3 = {"小明",19,100.0};
	/*
		两个相邻运算符优先级不同，看优先级
		两个相邻运算符优先级相同，看结合性
	*/
	return 0;
}


